package api

import (
	"fmt"
	"github.com/liserc/open-socket/pkg/common/authverify"
	"github.com/liserc/open-socket/pkg/common/servererrs"
	"github.com/liserc/open-socket/pkg/prommetrics"
	"github.com/liserc/open-socket/pkg/rpc"
	"github.com/openimsdk/tools/tokenverify"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"

	"github.com/gin-gonic/gin"
	"net/http"
	"strings"

	"github.com/openimsdk/protocol/constant"
	"github.com/openimsdk/tools/apiresp"
	"github.com/openimsdk/tools/discovery"
	"github.com/openimsdk/tools/log"
	"github.com/openimsdk/tools/mw"
)

func prommetricsGin() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()
		path := c.FullPath()
		if c.Writer.Status() == http.StatusNotFound {
			prommetrics.HttpCall("<404>", c.Request.Method, c.Writer.Status())
		} else {
			prommetrics.HttpCall(path, c.Request.Method, c.Writer.Status())
		}
		if resp := apiresp.GetGinApiResponse(c); resp != nil {
			prommetrics.APICall(path, c.Request.Method, resp.ErrCode)
		}
	}
}

func newGinRouter(registry discovery.SvcDiscoveryRegistry, config *Config) *gin.Engine {
	// init gin
	gin.SetMode(gin.ReleaseMode)
	router := gin.New()

	// init rpc client here
	registry.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
	messageRpc := rpc.NewMessageRpcClient(registry, config.Share.RpcRegisterName.Message)
	messageApi := NewMessageApi(messageRpc, config.Share.AdminUserID)
	authApi := NewAuthApi(config)

	// middleware
	router.Use(prommetricsGin(), gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID(), GinParseToken(config))

	// authentication
	authRouterGroup := router.Group("/auth")
	{
		authRouterGroup.POST("/user_token", authApi.UserToken)
		authRouterGroup.POST("/get_user_token", authApi.GetUserToken)
		authRouterGroup.POST("/parse_token", authApi.ParseToken)
	}

	// message
	messageGroup := router.Group("/message")
	{
		messageGroup.POST("/send_msg", messageApi.SendMessage)
		messageGroup.POST("/send_business_notification", messageApi.SendBusinessNotification)
	}

	return router
}

func GinParseToken(config *Config) gin.HandlerFunc {
	return func(c *gin.Context) {
		switch c.Request.Method {
		case http.MethodPost:
			for _, wApi := range Whitelist {
				if strings.HasPrefix(c.Request.URL.Path, wApi) {
					c.Next()
					return
				}
			}

			token := c.Request.Header.Get(constant.Token)
			if token == "" {
				log.ZWarn(c, "header get token error", servererrs.ErrArgs.WrapMsg("header must have token"))
				apiresp.GinError(c, servererrs.ErrArgs.WrapMsg("header must have token"))
				c.Abort()
				return
			}

			claims, err := tokenverify.GetClaimFromToken(token, authverify.Secret(config.Share.Secret))
			if err != nil {
				apiresp.GinError(c, err)
				c.Abort()
				return
			}

			c.Set(constant.OpUserPlatform, constant.PlatformIDToName(claims.PlatformID))
			c.Set(constant.OpUserID, claims.UserID)
			c.Next()
		}
	}
}

// Whitelist api not parse token
var Whitelist = []string{
	"/user/user_register",
	"/auth/user_token",
	"/auth/parse_token",
}
